package com.evil.account.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evil.account.api.RemoteUserRoleService;
import com.evil.account.mapper.RoleMapper;
import com.evil.account.mapper.UserRoleMapper;
import com.evil.account.pojo.entity.Role;
import com.evil.account.pojo.entity.UserRole;
import com.evil.account.service.UserEnterpriseService;
import com.evil.account.service.UserRoleService;
import com.evil.common.account.dto.FindByIdsAndEIdReqDTO;
import com.evil.common.account.dto.user.UserBaseRespDTO;
import com.evil.common.account.dto.user.UserEnterpriseBaseDTO;
import com.evil.common.account.dto.user.role.RoleUsersReqDTO;
import com.evil.common.account.dto.user.role.UserRoleBaseRespDTO;
import com.evil.common.account.util.LoginUtil;
import com.evil.common.core.enums.SwitchEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.ArrayCompareUtil;
import com.evil.common.family.dto.FindByIdReqDTO;
import com.evil.common.family.dto.FindByIdsReqDTO;
import com.evil.common.family.enums.RCodeEnum;
import com.evil.common.redis.util.RedisUtil;
import com.evil.common.rocketmq.annotation.MqCache;
import com.evil.common.rocketmq.constant.MQConstant;
import com.evil.common.rocketmq.entity.MsgCache;
import com.evil.common.rocketmq.enums.EnterpriseMqOpTagEnum;
import com.evil.common.rocketmq.util.MqRedisKeyUtil;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.function.Function;

/**
 * 角色表
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@RestController
@AllArgsConstructor
public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> implements UserRoleService, RemoteUserRoleService {

    private final LoginUtil loginUtil;

    private final RedisUtil redisUtil;

    private final RoleMapper roleMapper;

    private final UserEnterpriseService userEnterpriseService;

    private final AccountLogServiceImpl logService;

    /**
     * 获取企业用户角色列表
     *
     * @param userEnterpriseDTO userEnterpriseDTO
     * @return UserRole 列表
     */
    @Override
    public List<UserRole> findByEnterpriseUser(UserEnterpriseBaseDTO userEnterpriseDTO) {
        return this.baseMapper.findByUserId(userEnterpriseDTO.getEnterpriseId(), userEnterpriseDTO.getUserId(), true);
    }

    /**
     * 用户角色基础信息
     *
     * @param userEnterpriseDTO userEnterpriseDTO
     * @return RoleBaseRespDTO 列表
     */
    @Override
    public List<UserRoleBaseRespDTO> userRoleBases(UserEnterpriseBaseDTO userEnterpriseDTO) {
        return this.baseMapper.findByUserEnterprise(userEnterpriseDTO);
    }

    /**
     * 用户角色基础信息
     *
     * @param findByIdsAndEIdReqDTO findByIdsAndEIdReqDTO
     * @return RoleBaseRespDTO 列表
     */
    @Override
    public List<UserRoleBaseRespDTO> findUserRoleBaseByUserIds(FindByIdsAndEIdReqDTO findByIdsAndEIdReqDTO) {
        return this.baseMapper.findUserRoleBaseByUserIds(findByIdsAndEIdReqDTO);
    }

    /**
     * 用户角色基础信息
     *
     * @param findByIdsAndEIdReqDTO findByIdsAndEIdReqDTO
     * @return RoleBaseRespDTO 列表
     */
    @Override
    public Map<Long, List<UserRoleBaseRespDTO>> findGroupUserRoleBaseByUserIds(FindByIdsAndEIdReqDTO findByIdsAndEIdReqDTO) {
        return this.baseMapper.findGroupUserRoleBaseByUserIds(findByIdsAndEIdReqDTO);
    }

    /**
     * 修改用户角色（覆盖性）
     *
     * @param enterpriseId enterpriseId
     * @param userId       userId
     * @param roleIds      roleIds
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void modify(Long enterpriseId, Long userId, List<Long> roleIds) {
        if (CollectionUtil.isNotEmpty(roleIds)) {
            List<Role> roleList = roleMapper.findByIds(roleIds, true);
            if (roleList.size() != roleIds.size()) {
                throw new BusinessException(RCodeEnum.ROLE_NOT_EXIST);
            }
        } else {
            roleIds = new ArrayList<>();
        }
        List<UserRole> userRoles = this.baseMapper.findByUserId(enterpriseId, userId, false);

        this.compare(userRoles, roleIds, UserRole::getRoleId, dto -> dto, dto -> this.createUserRole(enterpriseId, userId, dto));
    }

    /**
     * 获取指定角色 用户id
     *
     * @param findByIdReqDTO findByIdReqDTO
     * @return userIds
     */
    @Override
    public List<Long> findUserIdsByRoleId(FindByIdReqDTO findByIdReqDTO) {
        return this.baseMapper.findUserIdsByRoleId(findByIdReqDTO.getId(), findByIdReqDTO.getFilterDeleted());
    }

    /**
     * 获取指定角色 用户基础信息列表
     *
     * @param findByIdsReqDTO findByIdsReqDTO
     * @return userIds
     */
    @Override
    public List<UserBaseRespDTO> findUserBasesByRoleIds(FindByIdsReqDTO findByIdsReqDTO) {
        return this.baseMapper.findUserBasesByRoleIds(findByIdsReqDTO);
    }

    /**
     * 获取指定角色 用户ids
     *
     * @param findByIdsReqDTO findByIdsReqDTO
     * @return userIds
     */
    @Override
    public List<Long> findUserIdsByRoleIds(FindByIdsReqDTO findByIdsReqDTO) {
        return this.baseMapper.findUserIdsByRoleIds(findByIdsReqDTO);
    }

    /**
     * 角色批量删除用户
     *
     * @param roleUsersReqDTO roleUsersReqDTO
     */
    @Override
    @MqCache
    @GlobalTransactional(rollbackFor = Exception.class)
    public void deleteUserRoleBatch(RoleUsersReqDTO roleUsersReqDTO) {
        roleUsersReqDTO.setEnterpriseId(loginUtil.getParamEnterpriseId(roleUsersReqDTO.getEnterpriseId()));
        // 检查企业管理员
        userEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        List<Long> userIds = roleUsersReqDTO.getUserIds();

        Role role = roleMapper.findById(roleUsersReqDTO.getRoleId());
        // 检查企业
        loginUtil.checkEnterpriseId(role.getEnterpriseId(), roleUsersReqDTO.getEnterpriseId());

        List<UserRole> userRoles = this.baseMapper.findByRIdAndUIds(role.getRoleId(), userIds, true);
        userRoles.forEach(ur -> ur.setIsDeleted(SwitchEnum.YES.getId()));
        logService.saveBatchAndCheck(this.baseMapper, UserRole::getId, userRoles);

        // 缓存MQ消息
        String key = MqRedisKeyUtil.requestUuidMqMsgCacheKey();
        userRoles.forEach(userRole -> {
            String uuid = UUID.fastUUID().toString(true);
            String destination = EnterpriseMqOpTagEnum.DELETE.topicAndTag(MQConstant.ENTERPRISE_ROLE_USER_MQ_KEY);
            UserRoleBaseRespDTO base = BeanUtil.copyProperties(userRole, UserRoleBaseRespDTO.class);
            boolean lpush = redisUtil.lpush(key, new MsgCache(uuid, destination, base));
            log.info("role user delete|enterprise:{}, role:{}, userId:{}, uuid:{}, destination:{}, lpush:{}",
                    base.getEnterpriseId(), base.getRoleId(), base.getUserId(), uuid, destination, lpush);
        });
    }

    /**
     * 角色批量新增用户
     *
     * @param roleUsersReqDTO roleUsersReqDTO
     */
    @Override
    @MqCache
    @GlobalTransactional(rollbackFor = Exception.class)
    public void addUserRoleBatch(RoleUsersReqDTO roleUsersReqDTO) {
        roleUsersReqDTO.setEnterpriseId(loginUtil.getParamEnterpriseId(roleUsersReqDTO.getEnterpriseId()));
        // 检查企业管理员
        userEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        Long roleId = roleUsersReqDTO.getRoleId();
        Long enterpriseId = roleUsersReqDTO.getEnterpriseId();
        Set<Long> userIds = new HashSet<>(roleUsersReqDTO.getUserIds());

        Role role = roleMapper.findById(roleId);
        // 检查企业
        loginUtil.checkEnterpriseId(role.getEnterpriseId(), enterpriseId);

        List<UserRole> userRoles = this.baseMapper.findByRIdAndUIds(role.getRoleId(), userIds, false);

        this.compare(userRoles, userIds, UserRole::getUserId, dto -> dto, dto -> this.createUserRole(enterpriseId, dto, roleId));
    }

    @MqCache
    @GlobalTransactional(rollbackFor = Exception.class)
    public <DTO> void compare(List<UserRole> userRoles, Collection<DTO> dtoList, Function<UserRole, Long> getEntityId, Function<DTO, Long> getDtoUserId, ArrayCompareUtil.CreateFunc<DTO, UserRole> createFunc) {
        Map<String, UserRole> mqMap = new HashMap<>();
        List<UserRole> modifies = ArrayCompareUtil.compare(
                userRoles,
                dtoList,
                getEntityId,
                getDtoUserId,
                UserRole::getIsDeleted,
                UserRole::setIsDeleted,
                (ur, dto, add) -> {
                    if (add) {
                        // 收集MQ: 新增/编辑
                        mqMap.put(EnterpriseMqOpTagEnum.ADD.topicAndTag(MQConstant.ENTERPRISE_ROLE_USER_MQ_KEY), ur);
                        return true;
                    }
                    return false;
                },
                (ur, delete) -> {
                    // 收集MQ: 新增/编辑
                    mqMap.put(EnterpriseMqOpTagEnum.DELETE.topicAndTag(MQConstant.ENTERPRISE_ROLE_USER_MQ_KEY), ur);
                    return false;
                },
                createFunc
        );

        logService.saveBatchAndCheck(this.baseMapper, UserRole::getId, modifies);

        // 缓存MQ消息
        String key = MqRedisKeyUtil.requestUuidMqMsgCacheKey();
        mqMap.forEach((destination, userRole) -> {
            String uuid = UUID.fastUUID().toString(true);
            UserRoleBaseRespDTO base = BeanUtil.copyProperties(userRole, UserRoleBaseRespDTO.class);
            boolean lpush = redisUtil.lpush(key, new MsgCache(uuid, destination, base));
            log.info("role user compare|enterprise:{}, role:{}, userId:{}, uuid:{}, destination:{}, lpush:{}",
                    base.getEnterpriseId(), base.getRoleId(), base.getUserId(), uuid, destination, lpush);
        });
    }

    private UserRole createUserRole(Long enterpriseId, Long userId, Long roleId) {
        UserRole userRole = new UserRole();
        userRole.setEnterpriseId(enterpriseId);
        userRole.setUserId(userId);
        userRole.setRoleId(roleId);
        userRole.setIsEnable(SwitchEnum.NO.getId());
        userRole.setIsDeleted(SwitchEnum.NO.getId());
        return userRole;
    }
}
